# @Time: 2021/12/21
# @Dev: TJ

import os
import sys
import re
import time
import requests
from lxml import etree
from loguru import logger
from pydantic import BaseModel, Field

sys.path.append(os.path.abspath('.'))
from src.sub_xpath.sub_xpath import xpath_string

re_sub_s = re.compile(r'\s')  # 匹配特殊符
re_search_f = re.compile(r'\d+(\.\d+)?', re.A)  # 匹配整数、小数


def is_number(s):
    """ 判断是否为数字字符串 """
    try:
        float(s)
        return True
    except Exception as e:
        logger.error(f'{e} : {s}')
        pass
    return False


def check_price(price, retain: int = 2) -> str:
    """
    正则 价格
    :param price: 价格字符串
    :param retain: 保留两位小数
    :return: 正常价格
    """
    if not price:
        return price

    price = re_sub_s.sub('', str(price).replace(',', ''))
    res = re_search_f.search(price)  # 正则小数或数字
    price = res.group() if res else ''
    new_price = str(round(float(price), retain)) if is_number(price) else ''  # 单位：元

    return new_price


class ExchangeRateInfo(BaseModel):
    ago_code: str = Field(default='CNY')  # 换算前的单位
    later_code: str = Field(default='CNY')  # 换算后的单位
    exchange_rate: float = Field(default=1.0)  # 汇率
    update_time: int = Field(default=int(time.time()))  # 更新时间


class ExchangeRate:

    __query_url = 'https://themoneyconverter.com/CN/{ago}/{later}'  # 查询网址

    # 查询间隔时间
    __query_interval = 60 * 60 * 24

    # 查询汇率信息存储
    __exchange_rate_info = dict(
        CNY=ExchangeRateInfo(ago_code='CNY', exchange_rate=1.0),  # 人民币 -> 人民币
        # USD=ExchangeRateInfo(ago_code='USD', exchange_rate=6.3457),  # 美元 -> 人民币
        # RUB=ExchangeRateInfo(ago_code='RUB', exchange_rate=0.086340),  # 卢布 -> 人民币
    )

    @classmethod
    def get_exchange_rate(cls, ago_code: str = "USD", later_code: str = "CNY") -> float:
        """
        获取汇率
        :param ago_code: 转换前的单位
        :param later_code: 转换后的单位
        :return: 汇率
        """
        info: ExchangeRateInfo = cls.__exchange_rate_info.get(ago_code)

        # 首次查询
        if info is None:
            exchange_rate = cls.__req_exchange_rate(ago_code, later_code)
            if not exchange_rate:
                return 1.0

            exchange_rate = float(exchange_rate)

            cls.__exchange_rate_info.update({
                ago_code: ExchangeRateInfo(
                    ago_code=ago_code,
                    later_code=later_code,
                    exchange_rate=exchange_rate)
            })

            return exchange_rate

        # 过期更新
        if (int(time.time()) - info.update_time) > cls.__query_interval:
            exchange_rate = cls.__req_exchange_rate(ago_code, later_code)
            if not exchange_rate:
                return info.exchange_rate

            exchange_rate = float(exchange_rate)

            info.exchange_rate = exchange_rate
            info.update_time = int(time.time())
            cls.__exchange_rate_info.update({
                ago_code: info
            })

            return exchange_rate

        logger.debug(f'【缓存汇率】1 {ago_code} = {info.exchange_rate} {later_code}')
        return info.exchange_rate

    @classmethod
    def __req_exchange_rate(cls, ago_code: str = "USD", later_code: str = "CNY"):
        """ 获取实时汇率 """

        try:
            resp = requests.get(cls.__query_url.format(ago=ago_code, later=later_code))
            if resp.status_code != 200:
                logger.warning(f'获取实时汇率 {resp} {resp.url}')
                return

        except Exception as e:
            logger.error(f'获取实时汇率 {e}')
            return

        html = etree.HTML(resp.text)
        result = xpath_string(html, '//div[@id="cc-ratebox"]', scrapy=False)
        price = check_price(result, retain=4)
        exchange_rate = price if price else None
        logger.debug(f'【实时汇率】1 {ago_code} = {exchange_rate} {later_code}')

        return exchange_rate


if __name__ == '__main__':
    print(ExchangeRate.get_exchange_rate())
    print(ExchangeRate.get_exchange_rate(ago_code='RUB'))
    print(ExchangeRate.get_exchange_rate(ago_code='CNY'))
    time.sleep(5)
    print(ExchangeRate.get_exchange_rate())
    print(ExchangeRate.get_exchange_rate(ago_code='RUB'))
    print(ExchangeRate.get_exchange_rate(ago_code='CNY'))
